package org.hit.burkun.extract;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.hit.burkun.db.DBHelper;
import org.hit.burkun.entities.Entity;
import org.hit.burkun.entities.realtions.BaseRealtion;
import org.hit.burkun.extract.FiledsToEntityMapper.FILED;
import org.hit.burkun.umls.GeneralQuery;
//import org.hit.burkun.mapping.BlockOnlineMapping;
//import org.hit.burkun.mapping.IOnlineMappingProcessor.DBKEY;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NoUseSqlRelExtracter extends RelationExtracter {

	private DBHelper dbh;
	private FiledsToEntityMapper ent1mapper;
	private FiledsToEntityMapper ent2mapper;
	private FiledsToRealtionMapper realtionmapper;
	// 使用 limit
	private int curConter = 0;
	protected int defualtBatchNum = 200;

	private static Logger logger = LoggerFactory
			.getLogger(NoUseSqlRelExtracter.class);

	public NoUseSqlRelExtracter(DBHelper dbh, FiledsToEntityMapper entity1,
			FiledsToEntityMapper entity2, FiledsToRealtionMapper realtion) {
		this.dbh = dbh.clone(); // 获取clone的属性
		this.ent1mapper = entity1;
		this.ent2mapper = entity2;
		this.realtionmapper = realtion;
	}

	public NoUseSqlRelExtracter(DBHelper dbh, FiledsToEntityMapper entity,
			FiledsToRealtionMapper realtion) {
		this(dbh, entity, entity, realtion);
	}

	@Override
	public void bengin() {
		logger.debug("connect to db...");
		this.dbh.connectionDB();
		cacheMap.clear();
		isdone = false;

	}

	@Override
	public void end() {
		logger.debug("disconnect to db...");
		this.dbh.disconnection();
	}

	// 首先从数据库中抽取relation，然后再从term中抽取term定义，这种类型的只适用于Ontology
	private boolean isdone = false;
	private Map<String, Entity> cacheMap = new HashMap<>();
	private Map<String, Set<String>> cacheUIDMap = new HashMap<>();

	@Override
	public Collection<BaseRealtion> getNextBatchRealtions(int num) {
		logger.debug("get next " + num + " batch...");
		Collection<BaseRealtion> returnres = new LinkedList<>();
		if (!isdone) {
			String queryStrTemp = realtionmapper.getQueryTemplete()
					+ " limit %d, %d";
			int ient1 = realtionmapper
					.getResultIdxByFiled(org.hit.burkun.extract.FiledsToRealtionMapper.FILED.ENT1);
			int ient2 = realtionmapper
					.getResultIdxByFiled(org.hit.burkun.extract.FiledsToRealtionMapper.FILED.ENT2);
			int iscore = realtionmapper
					.getResultIdxByFiled(org.hit.burkun.extract.FiledsToRealtionMapper.FILED.SCORE);
			int idbid = realtionmapper
					.getResultIdxByFiled(org.hit.burkun.extract.FiledsToRealtionMapper.FILED.DBID);
			int idesc = realtionmapper
					.getResultIdxByFiled(org.hit.burkun.extract.FiledsToRealtionMapper.FILED.DESC);
			int icalcmethod = realtionmapper
					.getResultIdxByFiled(org.hit.burkun.extract.FiledsToRealtionMapper.FILED.CALC_METHOD);

			String queryWithData = String.format(queryStrTemp, curConter, num);

			// System.out.println(queryWithData);

			Collection<String[]> res = dbh.queryData(queryWithData,
					realtionmapper.getFiledsSize());
			Map<String, String> ent1 = new HashMap<>();
			Map<String, String> ent2 = new HashMap<>();

			// 需要for两边才能生成relationship
			for (String[] line : res) {
				// 变换
				ent1.put(getMapKey(ent1mapper.getType(), line[ient1]),
						line[ient1]);
				ent2.put(getMapKey(ent2mapper.getType(), line[ient2]),
						line[ient2]);
			}
			// 做缓存
			cacheUID(ent1.values(), ent1mapper);
			cacheUID(ent2.values(), ent2mapper);
			// 做缓存啊
			cacheEntity(ent1, ent1mapper);
			cacheEntity(ent2, ent2mapper);

			// 再次遍历
			for (String[] line : res) {
				String id1 = line[ient1];
				String id2 = line[ient2];
				String score = iscore != -1 ? line[iscore] : null;
				String dbid = idbid != -1 ? line[idbid] : null;
				String desc = idesc != -1 ? line[idesc] : null;
				String calcmethod = icalcmethod != -1 ? line[icalcmethod]
						: null;
				String key1 = getMapKey(ent1mapper.getType(), id1);
				String key2 = getMapKey(ent2mapper.getType(), id2);

				if (cacheMap.containsKey(key1) && cacheMap.containsKey(key2)) {
					BaseRealtion brel = new BaseRealtion(cacheMap.get(key1),
							cacheMap.get(key2), realtionmapper.isDirect(),
							realtionmapper.getType());
					brel.setCalcMethod(calcmethod);
					if (score != null) {
						brel.setConfidscore(Double.parseDouble(score));
					} else {
						brel.setConfidscore(null);
					}
					brel.setDbid(dbid);
					brel.setDesc(desc);
					returnres.add(brel);
				}
			}
			if (res.size() < num) {
				// 取完了
				isdone = true;
			}
			curConter += num;
		}
		return returnres;

	}

	public Collection<BaseRealtion> getNextBatchRealtions() {
		return getNextBatchRealtions(defualtBatchNum);
	}

	// 把实体添加到map中
	private void cacheEntity(Map<String, String> dic,
			FiledsToEntityMapper mapper) {
		// remove the id that in the map, reduce map
		logger.debug("add Entity to cacheEntity...");
		Set<String> entityID = new HashSet<>();
		Set<Entry<String, String>> enties = dic.entrySet();
		for (Entry<String, String> entry : enties) {
			if (!cacheMap.containsKey(entry.getKey())) {
				entityID.add(entry.getValue());
			}
		}

		if (entityID.size() > 0) {
			// 不是假的才查数据库
			// 获取uid, Map<String, Set<String>>
			if (!mapper.isFake()) {
				String queryStr = mapper.buildQueryWithData(entityID);
				// System.out.println(queryStr);
				Collection<String[]> data = dbh.queryData(queryStr,
						mapper.getFiledsSize());
				int idbid = mapper.getResultIdxByFiled(FILED.DBID);
				int iname = mapper.getResultIdxByFiled(FILED.NAME);
				int idef = mapper.getResultIdxByFiled(FILED.DEF);
				int iuid = mapper.getResultIdxByFiled(FILED.UID);
				int iprimary_id = mapper.getResultIdxByFiled(FILED.PRIMARY_ID);
				// 获取uid
				Entity.TYPE type = mapper.getType();
				for (String[] line : data) {
					String dbid = line[idbid];
					String prid = line[iprimary_id];
					String name = (iname != -1) ? line[iname] : null;
					String def = (idef != -1) ? line[idef] : null;
					String uid = (iuid != -1) ? line[iuid] : null;
					// map uid
					if (uid == null) {
						String mapid = getMapKey(type, prid);
						if (cacheUIDMap.containsKey(mapid)) {
							Set<String> uids = cacheUIDMap.get(mapid);
							// 多出的uid用逗号分割
							uid = StringUtils.join(uids, ",");
						}
					}
					Entity entity = new Entity(prid, name, uid, dbid, type);
					entity.setDef(def);

					cacheMap.put(getMapKey(mapper.getType(), prid), entity);
				}
			} else {
				// 是假的就直接返回
				for (String id : entityID) {
					// 处理id的mapping问题
					Entity en = new Entity(null, id, "", id, mapper.getType());
					String mapid = getMapKey(mapper.getType(), id);
					if (cacheUIDMap.containsKey(mapid)) {
						Set<String> uids = cacheUIDMap.get(mapid);
						// 多出的uid用逗号分割
						en.setUid(StringUtils.join(uids, ","));
					}
					cacheMap.put(getMapKey(mapper.getType(), id), en);
				}
			}

		}
	}

	private void cacheUID(Collection<String> data,
			FiledsToEntityMapper entmapper) {
		// 构造出一个假的mapping表
		logger.debug("add Id to cacheUID...");
		Entity.TYPE type = entmapper.getType();
		GeneralQuery gq = new GeneralQuery(DBHelper.getServerData(),
				"map_umls_ids");
		gq.begin();
		switch (type) {
		case GENE_ID:
		case UNIPORT_AC: {
			// uid type umls or umap
			gq.quickMap(data, type);
			Map<String, Set<String>> res = gq.getResult();
			Set<Entry<String, Set<String>>> enties = res.entrySet();
			for (Entry<String, Set<String>> entry : enties) {
				cacheUIDMap.put(getMapKey(type, entry.getKey()),
						entry.getValue());
			}
			break;
		}
		case DISEASE_ONTOLOGY:
		case GENE_ONTOLOGY:
		case HP_ONTOLOGY: {
			// uid type umls or umap
			// 现将这个东西变换称DO:ID
			Map<String, String> accID = getOntologyAcc2ID(entmapper, data);
			gq.quickMap(accID.keySet(), type);
			Map<String, Set<String>> res = gq.getResult();
			Set<Entry<String, Set<String>>> enties = res.entrySet();
			for (Entry<String, Set<String>> entry : enties) {
				cacheUIDMap.put(getMapKey(type, accID.get(entry.getKey())),
						entry.getValue());
			}
			break;
		}
		default:
			// 构造出一个假的mapping表
			for (String id : data) {
				Set<String> hashset = new HashSet<>();
				hashset.add(id);
				cacheUIDMap.put(getMapKey(type, id), hashset);
			}
		}
		gq.end();
	}

	// 如果是本体，那么需要查找出相应的disease acc,返回 id对应的acc, 这样做有些浪费，不过也只能这样做了
	private Map<String, String> getOntologyAcc2ID(FiledsToEntityMapper mapper,
			Collection<String> ids) {
		String queryStr = mapper.buildQueryWithData(ids);
		Collection<String[]> data = dbh.queryData(queryStr,
				mapper.getFiledsSize());
		Map<String, String> map = new HashMap<>();
		int idbid = mapper.getResultIdxByFiled(FILED.DBID);
		int iprimary_id = mapper.getResultIdxByFiled(FILED.PRIMARY_ID);

		for (String[] line : data) {
			String dbid = line[idbid];
			String prid = line[iprimary_id];
			map.put(dbid, prid);
		}
		return map;
	}

	private String getMapKey(Entity.TYPE type, String id) {
		return type.getTag() + ":" + id;
	}

	public boolean isDone() {
		return isdone;
	}

}
